package cn.xyinping.user.service.impl;

import cn.xyinping.basic.exception.MyException;
import cn.xyinping.basic.util.AjaxResult;
import cn.xyinping.basic.util.HttpClientUtils;
import cn.xyinping.basic.util.MD5Utils;
import cn.xyinping.basic.util.StrUtils;
import cn.xyinping.user.constant.UserConstant;
import cn.xyinping.user.domain.Logininfo;
import cn.xyinping.user.domain.User;
import cn.xyinping.user.domain.WxUser;
import cn.xyinping.user.dto.LoginDto;
import cn.xyinping.user.mapper.LogininfoMapper;
import cn.xyinping.user.mapper.UserMapper;
import cn.xyinping.user.mapper.WxUserMapper;
import cn.xyinping.user.service.ILoginService;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 员工登陆
 * @author 27266
 * @desrciption
 * @date 2021/10/30
 */
@Service
public class LoginServiceImpl implements ILoginService {
    @Autowired
    private LogininfoMapper logininfoMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private WxUserMapper wxUserMapper;
    @Autowired
    private UserMapper userMapper;
    @Override
    public Map<String, Object> account(LoginDto loginDto) {
        // 非空判断
        if(StringUtils.isEmpty(loginDto.getUsername())
                ||StringUtils.isEmpty(loginDto.getPassword())
                ||StringUtils.isEmpty(loginDto.getType())){
            throw new MyException("参数不能为空");
        }
        // 根据用户名和类型去查询logininfo
        Logininfo logininfo = logininfoMapper.findByUsernameAndType(loginDto);
        // 查不到提示用户信息不存在
        if(logininfo==null){
            throw new MyException("该用户不存在");
        }
        // 查得到，判断密码是否一致，不一致提示密码错误
        String password = logininfo.getPassword(); // 数据库中的密码都是加密加盐了的所以也要把前端传来的密码也加密加盐才比较
        String salt = logininfo.getSalt();
        String encryp = MD5Utils.encrypByMd5(loginDto.getPassword() + salt);
        if(!password.equals(encryp)){
            throw new MyException("密码错误");
        }
        // 如果没有问题，把用户信息存入redis中
        String token = UUID.randomUUID().toString();   // UUID保证生成的值用不重复，用来做Key  value:用户信息
        // 对象存储到redis需要序列化
        redisTemplate.opsForValue().set(token, logininfo, 30, TimeUnit.MINUTES);
        // 把token  用户信息logininfo存到 map
        HashMap<String, Object> map = new HashMap<>();
        map.put("token", token);
        // 前台不能出现盐值和密码所以要设为null
        logininfo.setPassword(null);
        logininfo.setSalt(null);
        map.put("loginInfo", logininfo);
        return map;
    }

    /**
     * 微信登陆
     * @param map
     * @return
     */
    @Override
    public AjaxResult wechat(Map<String, String> map) {
        // 获取key code
        String code = map.get("code");
        // 判断授权码code不为空
//        StringUtils.hasLength() 根据长度判断是不是为空  空返回是false
        if(StringUtils.isEmpty(code)){
            throw new MyException("授权码不存在");
        }

        // 通过code获取令牌token  获取的是json格式的字符串
        String strObj = HttpClientUtils.httpGet(UserConstant.TOKEN_BY_CODE
                .replace("APPID", UserConstant.APPID)
                .replace("SECRET", UserConstant.SECRET)
                .replace("CODE", code));// 替换把TOKEN_BY_CODE中APPID，SECRET,CODE替换成新的有参数的
        // 把json格式字符串转为对象
        JSONObject jsonObject = JSONObject.parseObject(strObj);
        // 获取token 令牌
        Object accessToken  = jsonObject.get("access_token");
        // 获取到openId
        String openId = jsonObject.getString("openid");

        // 根据openid查询wxuser表
        WxUser wxUser = wxUserMapper.loadByOpenId(openId);
        // 判断wxUser是否为空 如果不为空，并且user_id存在，免密登陆
        if(wxUser!=null && wxUser.getUser_id()!=null){ // 用户以前扫描过
            // 登陆成功，向redis中logininfo（用户信息）保存信息
            // 通过user_id 查询logininfo
            Logininfo logininfo = logininfoMapper.findByUserId(wxUser.getUser_id());
            // 如果没有问题，把用户信息存在redis
            String token = UUID.randomUUID().toString();
            //  对象存储到redis，需要序列化
            redisTemplate.opsForValue().set(
                    token,
                    logininfo,
                    30,
                    TimeUnit.MINUTES);

            Map<String, Object> map1 = new HashMap<>();
            // map中应该存token， 用户信息
            map1.put("token",token);

            //  不能让前端看到用户机密信息
            logininfo.setSalt(null);
            logininfo.setPassword(null);
            map1.put("logininfo", logininfo);

            return AjaxResult.me().setResultObj(map1);
        }else { // 当前没有扫描过(wxuser 中没有绑定)
              // 把token和openid拼接一下
            String param = "?accessToken="+ accessToken+"&openId="+openId;
            // 返回token openId给前端
            return AjaxResult.me().setSuccess(false).setResultObj(param);
        }
    }

    /**
     * 微信绑定业务
     * @param param
     * @return
     */
    @Override
    public Map<String, Object> binder(Map<String, String> param) {
        // 获取四个参数
        String phone = param.get("phone");
        String verifyCode = param.get("verifyCode");
        String accessToken = param.get("accessToken");
        String openId = param.get("openId");
        // 判断四个参数不为空
        if(StringUtils.isEmpty(phone)
                ||StringUtils.isEmpty(verifyCode)
                ||StringUtils.isEmpty(accessToken)
                ||StringUtils.isEmpty(openId)){
            throw new MyException("参数不能为空");
        }
        // 判断验证码是否过期
        Object obj = redisTemplate.opsForValue().get(UserConstant.BINDER_PREFIX + phone); // 根据手机号获取验证码
        if(obj==null){ // 验证码过期
            throw new MyException("验证码过期了，请重新获取");
        }else { // 没有过期，从redis中获取验证码和用户输入的验证码进行对比
            String code = obj.toString().split(":")[1]; // 截取出验证码
            if(!code.equalsIgnoreCase(verifyCode)){
                throw new MyException("验证码输入不正确");
            }
        }
        // 判断手机号是否注册（以前是否扫描过）
        User tmpUser = userMapper.findByPhone(phone);
        User user = null;
        Logininfo logininfo = null;
        if(tmpUser!=null){ // 注册过就直接绑定
            user = tmpUser;
            // 根据user表中logininfo_id查询出logininfo
            logininfo = logininfoMapper.findByUserId(user.getId());
        }else { // 没有被注册过
            // 通过电话号码获取user
            user = initUser(phone);
            // 根据user获取logininfo
            logininfo = initLogininfo(user);
            // 先保存logininfo再根据logininfo自增主键去添加user
            logininfoMapper.add(logininfo);
            user.setLogininfo_id(logininfo.getId());
            userMapper.add(user);
        }
        // 从微信端获取wxuser  根据token openid
        String wxuser = UserConstant.WX_USER.replace("ACCESS_TOKEN", accessToken).replace("OPENID", openId);
        // 发请求
        String strObj = HttpClientUtils.httpGet(wxuser);
        // 把扫描用户信息添加到wxUser表中
        WxUser wxUser = initWxUser(strObj, user);
        wxUserMapper.add(wxUser);
        // 把登录成功的用户信息保存到redis，并且返回map给前端
        String token = UUID.randomUUID().toString(); // // 如果没有问题，把用户信息存入redis中
        //  对象存储到redis，需要序列化
        redisTemplate.opsForValue().set(
                token,
                logininfo,
                30,
                TimeUnit.MINUTES);

        Map<String, Object> map = new HashMap<>();
        // map中应该存token， 用户信息
        map.put("token",token);

        //  不能让前端看到用户机密信息
        logininfo.setSalt(null);
        logininfo.setPassword(null);
        map.put("logininfo", logininfo);

        return map;
    }

    /**
     * 获取wx_user
     * @param strObj
     * @param user
     * @return
     */
    private WxUser initWxUser(String strObj, User user) {
        // 把json格式字符串转为对象
        JSONObject jsonObject = JSONObject.parseObject(strObj);
        WxUser wxUser = new WxUser();
        wxUser.setOpenid(jsonObject.getString("openid"));
        wxUser.setNickname(jsonObject.getString("nickname"));
        wxUser.setSex(jsonObject.getInteger("sex"));
        wxUser.setAddress(null);
        wxUser.setHeadimgurl(jsonObject.getString("headimgurl"));
        wxUser.setUnionid(jsonObject.getString("unionid"));
        wxUser.setUser_id(user.getId());
        return wxUser;
    }

    /**
     * 根据user获取logininfo
     * @param user
     * @return
     */
    private Logininfo initLogininfo(User user) {
        Logininfo logininfo = new Logininfo();
        // user中的数据copy到logininfo中
        BeanUtils.copyProperties(user, logininfo);
        // 普通用户
        logininfo.setType(1);
        return logininfo;
    }

    /**
     * 通过手机号获取user
     * @param phone
     * @return
     */
    private User initUser(String phone) {
        User user = new User();
        user.setUsername(phone); // 账号
        user.setPhone(phone);
        user.setState(1);
        //盐值
        String salt = StrUtils.getComplexRandomString(32);
        // 密码： 加密加盐
        String password = MD5Utils.encrypByMd5(1 + salt);
        user.setSalt(salt);
        user.setPassword(password);
        return user;
    }
}
